﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using CharacterCreation.Data.Models;
using Db4objects.Db4o;
using Db4objects.Db4o.Config;

namespace CharacterCreation.Data {
    /// <summary>
    /// The data context in which all the objects live.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DataContext {

        private static string connectionString = "C:\\DataBases\\charactercreation.yap";

        private static IObjectContainer context;
        private static IObjectContainer getContext() {
            if (context == null) {
                IEmbeddedConfiguration config = Db4oEmbedded.NewConfiguration();
                config.Common.ObjectClass(typeof(Character)).CascadeOnUpdate(true);
                config.Common.ObjectClass(typeof(Character)).CascadeOnActivate(true);

                context = Db4oEmbedded.OpenFile(config, connectionString);
            }
            return context;
        }

        private static T Get<T>(Guid id, IObjectContainer context) {
            try {
                return context.Query<T>(new Predicate<T>(t => (Guid)t.GetType().GetProperty("ID").GetValue(t, null) == id)).FirstOrDefault();
            }
            catch { }
            return default(T);
        }

        /// <summary>
        /// Returns a specific item from the database
        /// </summary>
        /// <typeparam name="T">The object's type</typeparam>
        /// <param name="id">The id of the object which needs to be returned</param>
        /// <returns>The object to be retrieved.</returns>
        public static T Get<T>(Guid id) {
            IObjectContainer context = getContext();
            try {
                T item = Get<T>(id, context);
                return item;
            }
            catch {
                return default(T);
            }
        }

        /// <summary>
        /// Adds an object to the database.
        /// </summary>
        /// <typeparam name="T">The object's type</typeparam>
        /// <param name="item">The item to be added</param>
        public static bool Store<T>(T item) {
            IObjectContainer context = getContext();
            try {
                if (typeof(BasePersistanceClass).IsAssignableFrom(typeof(T)))
                    (item as BasePersistanceClass).TimeStamp = DateTime.Now;

                context.Store(item);
                return true;
            }
            catch {
                context.Rollback();
                return false;
            }
        }

        /// <summary>
        /// Deletes an object.
        /// </summary>
        /// <typeparam name="T">The object's type</typeparam>
        /// <param name="item">The item to be deleted</param>
        public static bool Delete<T>(T item) {
            IObjectContainer context = getContext();
            try {
                context.Delete(item);
                return true;
            }
            catch {
                context.Rollback();
                return false;
            }
        }

        /// <summary>
        /// Returns all the objects in the database of a certain type.
        /// </summary>
        /// <typeparam name="T">The object's type</typeparam>
        /// <returns>A list of the current object.</returns>
        public static ObservableCollection<T> GetAll<T>(Predicate<T> where) {
            IObjectContainer context = getContext();
            try {
                if (where != null)
                    return new ObservableCollection<T>(context.Query<T>(where));
                else
                    return new ObservableCollection<T>(context.Query<T>());
            }
            catch {
                return null;
            }
        }

        /// <summary>
        /// Returns all the objects in the database of a certain type.
        /// </summary>
        /// <typeparam name="T">The object's type</typeparam>
        /// <returns>A list of the current object.</returns>
        public static ObservableCollection<T> GetAll<T>() {
            return GetAll<T>(null);
        }

        /// <summary>
        /// Releases the context after the application is stopped.
        /// </summary>
        public static void ReleaseAll() {
            context.Close();
            context.Dispose();
        }
    }
}
